#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <math.h>

#include "hi_common.h"
#include "hi_comm_sys.h"
#include "hi_comm_svp.h"
#include "sample_comm.h" 
#include "sample_comm_svp.h"
#include "sample_comm_nnie.h" 
#include "sample_svp_nnie_software.h"
#include "sample_comm_ive.h"


#include <time.h>
#include "retinanet.h"
 
static SAMPLE_SVP_NNIE_MODEL_S s_stRetinaModel = {0}; // 模型参数
static SAMPLE_SVP_NNIE_PARAM_S s_stRetinaNnieParam = {0}; // NNIE参数
static SVP_SOFT_RETINA_S stSoftParam = {0}; // soft infor

struct timespec start_time;
struct timespec end_time;


typedef enum RETINA_TASK_SELECT
{
    RetinaNet_aBGR_to_print_select,
    RetinaNet_BGR_to_List_select,
    RetinaNet_JPG_to_Vo_select, // 控制函数 e退出 p暂停 r恢复
}myRETINA_TASK_SELECT;

myRETINA_TASK_SELECT TASK_SELECT = RetinaNet_JPG_to_Vo_select; // 任务选择



/* *****************************************************

公用子代码

******************************************************* */

// 打印时间
double PrintTime(HI_CHAR* str, HI_BOOL print_total_time, HI_BOOL add_total_time, HI_BOOL print_single){
    static double use_time_total = 0; 
    double use_time = 0;
    clock_gettime(CLOCK_REALTIME, &end_time); // 计时
    use_time = (double)(end_time.tv_sec - start_time.tv_sec)*1000 + (end_time.tv_nsec - start_time.tv_nsec) / 1000000.0;
    if(add_total_time)
        use_time_total += use_time;
    if(PRINT_FLAG && print_single){
        printf("%s: %.5fms\n", str, use_time);
        if(print_total_time){
            printf("total time: %.5fms  FPS:%.5f\n", use_time_total, 1000/use_time_total);
        }
    }
    if(print_total_time){
        use_time_total = 0;
    }
    start_time.tv_sec = end_time.tv_sec;
    start_time.tv_nsec = end_time.tv_nsec;

    return use_time;
}

// 裁剪bbox，防止坐标超出图像
HI_FLOAT myClip(HI_FLOAT value){
	HI_FLOAT max = RETINA_INPUT_SIZE - 1;
	value = value > max ? max : value;
	value = value < 0 ? 0 : value;
	return value;
}


// 填充图片 (由于sample_nnie.c里的该代码无法放于头文件会互相引用产生冲突，故在这里重新复制一遍该代码)
static HI_S32 my_NNIE_FillSrcData(SAMPLE_SVP_NNIE_CFG_S* pstNnieCfg,
    SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam, SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S* pstInputDataIdx)
{
    FILE* fp = NULL;
    HI_U32 i =0, j = 0, n = 0;
    HI_U32 u32Height = 0, u32Width = 0, u32Chn = 0, u32Stride = 0, u32Dim = 0;
    HI_U32 u32VarSize = 0;
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U8*pu8PicAddr = NULL;
    HI_U32*pu32StepAddr = NULL;
    HI_U32 u32SegIdx = pstInputDataIdx->u32SegIdx;
    HI_U32 u32NodeIdx = pstInputDataIdx->u32NodeIdx;
    HI_U32 u32TotalStepNum = 0;

    /*open file*/
    if (NULL != pstNnieCfg->pszPic)
    {
        fp = fopen(pstNnieCfg->pszPic,"rb");
        SAMPLE_SVP_CHECK_EXPR_RET(NULL == fp,HI_INVALID_VALUE,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error, open file failed!\n");
    }

    /*get data size*/
    if(SVP_BLOB_TYPE_U8 <= pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].enType &&
        SVP_BLOB_TYPE_YVU422SP >= pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].enType)
    {
        u32VarSize = sizeof(HI_U8);
    }
    else
    {
        u32VarSize = sizeof(HI_U32);
    }

    /*fill src data*/
    if(SVP_BLOB_TYPE_SEQ_S32 == pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].enType)
    {
        u32Dim = pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].unShape.stSeq.u32Dim;
        u32Stride = pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u32Stride;
        pu32StepAddr = (HI_U32*)(pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].unShape.stSeq.u64VirAddrStep);
        pu8PicAddr = (HI_U8*)(pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u64VirAddr);
        for(n = 0; n < pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u32Num; n++)
        {
            for(i = 0;i < *(pu32StepAddr+n); i++)
            {
                s32Ret = fread(pu8PicAddr,u32Dim*u32VarSize,1,fp);
                SAMPLE_SVP_CHECK_EXPR_GOTO(1 != s32Ret,FAIL,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,Read image file failed!\n");
                pu8PicAddr += u32Stride;
            }
            u32TotalStepNum += *(pu32StepAddr+n);
        }
        SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u64PhyAddr,
            (HI_VOID *) pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u64VirAddr,
            u32TotalStepNum*u32Stride);
    }
    else
    {
        u32Height = pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].unShape.stWhc.u32Height;
        u32Width = pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].unShape.stWhc.u32Width;
        u32Chn = pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].unShape.stWhc.u32Chn;
        u32Stride = pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u32Stride;
        pu8PicAddr = (HI_U8*)(pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u64VirAddr);
        if(SVP_BLOB_TYPE_YVU420SP== pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].enType)
        {
            for(n = 0; n < pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u32Num; n++)
            {
                for(i = 0; i < u32Chn*u32Height/2; i++)
                {
                    s32Ret = fread(pu8PicAddr,u32Width*u32VarSize,1,fp);
                    SAMPLE_SVP_CHECK_EXPR_GOTO(1 != s32Ret,FAIL,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,Read image file failed!\n");
                    pu8PicAddr += u32Stride;
                }
            }
        }
        else if(SVP_BLOB_TYPE_YVU422SP== pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].enType)
        {
            for(n = 0; n < pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u32Num; n++)
            {
                for(i = 0; i < u32Height*2; i++)
                {
                    s32Ret = fread(pu8PicAddr,u32Width*u32VarSize,1,fp);
                    SAMPLE_SVP_CHECK_EXPR_GOTO(1 != s32Ret,FAIL,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,Read image file failed!\n");
                    pu8PicAddr += u32Stride;
                }
            }
        }
        else
        {
            for(n = 0; n < pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u32Num; n++)
            {
                for(i = 0;i < u32Chn; i++)
                {
                    for(j = 0; j < u32Height; j++)
                    {
                        s32Ret = fread(pu8PicAddr,u32Width*u32VarSize,1,fp);
                        SAMPLE_SVP_CHECK_EXPR_GOTO(1 != s32Ret,FAIL,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,Read image file failed!\n");
                        pu8PicAddr += u32Stride;
                    }
                }
            }
        }
        SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u64PhyAddr,
            (HI_VOID *) pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u64VirAddr,
            pstNnieParam->astSegData[u32SegIdx].astSrc[u32NodeIdx].u32Num*u32Chn*u32Height*u32Stride);
    }

    fclose(fp);
    return HI_SUCCESS;
FAIL:

    fclose(fp);
    return HI_FAILURE;
}


// NNIE forward (由于sample_nnie.c里的该代码无法放于头文件会互相引用产生冲突，故在这里重新复制一遍该代码)
static HI_S32 my_NNIE_Forward(SAMPLE_SVP_NNIE_PARAM_S *pstNnieParam,
    SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S* pstInputDataIdx,
    SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S* pstProcSegIdx,HI_BOOL bInstant)
{
    HI_S32 s32Ret = HI_SUCCESS;
    HI_U32 i = 0, j = 0;
    HI_BOOL bFinish = HI_FALSE;
    SVP_NNIE_HANDLE hSvpNnieHandle = 0;
    HI_U32 u32TotalStepNum = 0;

    SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64PhyAddr,
        (HI_VOID *) pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u64VirAddr,
        pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].stTskBuf.u32Size); // 清空Tskbuf的cache、或是将Tskbuf的内容输入内存中

    /*set input blob according to node name*/
    // 由于在硬件初始化时，仅分配了目标节点blob的空间，并未将源节点与目标节点对应，该步骤就是让他们对应起来
    if(pstInputDataIdx->u32SegIdx != pstProcSegIdx->u32SegIdx) // 如果输入数据的段 和正要处理的段 id不一样
    {
        // 就在解析的模型里面搜索，将输入段的目标节点与处理段的源节点对应起来
        for(i = 0; i < pstNnieParam->pstModel->astSeg[pstProcSegIdx->u32SegIdx].u16SrcNum; i++) // 遍历当前处理段的所有源节点
        {
            for(j = 0; j < pstNnieParam->pstModel->astSeg[pstInputDataIdx->u32SegIdx].u16DstNum; j++) // 遍历输入段的目标节点
            {
                if(0 == strncmp(pstNnieParam->pstModel->astSeg[pstInputDataIdx->u32SegIdx].astDstNode[j].szName,
                    pstNnieParam->pstModel->astSeg[pstProcSegIdx->u32SegIdx].astSrcNode[i].szName,
                    SVP_NNIE_NODE_NAME_LEN)) // 节点id相等
                {
                    pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astSrc[i] =
                        pstNnieParam->astSegData[pstInputDataIdx->u32SegIdx].astDst[j]; // 处理段源节点 由 输入段目标节点 相同(地址、跨度、blob的hwc、blob类型)
                    break;
                }
            }
            SAMPLE_SVP_CHECK_EXPR_RET((j == pstNnieParam->pstModel->astSeg[pstInputDataIdx->u32SegIdx].u16DstNum), // 未找到源节点所对应的目标节点
                HI_FAILURE,SAMPLE_SVP_ERR_LEVEL_ERROR,"Error,can't find %d-th seg's %d-th src blob!\n",
                pstProcSegIdx->u32SegIdx,i);
        }
    }

    /*NNIE_Forward*/
    s32Ret = HI_MPI_SVP_NNIE_Forward(&hSvpNnieHandle, // handle
        pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astSrc, // 源节点信息
        pstNnieParam->pstModel, pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst, // 目标节点信息
        &pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx], bInstant); // 控制参数  是否判断完成
    SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,HI_MPI_SVP_NNIE_Forward failed!\n");

    // 调用API 查询任务句柄以及对应的某个nnie的id是否完成了工作
    if(bInstant)
    {
        /*Wait NNIE finish*/
        while(HI_ERR_SVP_NNIE_QUERY_TIMEOUT == (s32Ret = HI_MPI_SVP_NNIE_Query(pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].enNnieId,
            hSvpNnieHandle, &bFinish, HI_TRUE)))
        {
            usleep(100);
            SAMPLE_SVP_TRACE(SAMPLE_SVP_ERR_LEVEL_INFO,
                "HI_MPI_SVP_NNIE_Query Query timeout!\n"); // 还有超时提示
        }
    }

    bFinish = HI_FALSE; // 清空nnie完成标志位
    // 由于nnie得到的结果存在cache里，需对每一个目标节点刷新cache
    for(i = 0; i < pstNnieParam->astForwardCtrl[pstProcSegIdx->u32SegIdx].u32DstNum; i++) // 遍历处理段的目标节点
    {
        if(SVP_BLOB_TYPE_SEQ_S32 == pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].enType)
        {
            for(j = 0; j < pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Num; j++)
            {
                u32TotalStepNum += *((HI_U32*)(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stSeq.u64VirAddrStep)+j);
            }
            SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr,
                (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,
                u32TotalStepNum*pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);

        }
        else // 节点blob类型为u8等
        {
            SAMPLE_COMM_SVP_FlushCache(pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64PhyAddr, // 目标节点 物理地址
                (HI_VOID *) pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u64VirAddr,// 虚拟地址
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Num* // batch * c * h * stride(不是w)
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Chn*
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].unShape.stWhc.u32Height*
                pstNnieParam->astSegData[pstProcSegIdx->u32SegIdx].astDst[i].u32Stride);
        }
    }

    return s32Ret;
}


// sort
void QuickSort_6(HI_FLOAT *nums, HI_S32 left, HI_S32 right){
	if(left>=right) return;
	if(left > DETECTION_RESULT_NUM_MAX*6) return;

//	cout << "left: " << left << "   right: " << right << endl;
	HI_S32 i=left, j=right; // i=left+1 is ok
	HI_FLOAT tmp;

	while(i<j){
		// big left, small right
		while(nums[j]<=nums[left] && i<j) j -= 6;
		while(nums[i]>=nums[left] && i<j) i += 6;

		if(i<j){
			for(int u=0; u<6; ++u){
				tmp = nums[i+u];
				nums[i+u] = nums[j+u];
				nums[j+u] = tmp;
			}
		}
	}
	for(HI_S32 u=0; u<6; ++u){
		tmp = nums[i+u];
		nums[i+u] = nums[left+u];
		nums[left+u] = tmp;
	}

	QuickSort_6(nums, left, i-6);
	QuickSort_6(nums, i+6, right);
}


// soft： 位置解算 + nms
HI_S32 Retinanet_Soft(SAMPLE_SVP_NNIE_PARAM_S *pstDetParam, SVP_SOFT_RETINA_S *pstSoftParam)
{
    HI_S32 s32Ret = HI_SUCCESS;

	// ***************       anchor init       ***************
	HI_S32 image_stride[RETINA_FPN_LAYER_NUM] = {8, 16, 32, 64, 128};
	HI_S32 anchor_size[RETINA_FPN_LAYER_NUM] = {32, 64, 128, 256, 512};
    // printf("\nget anchor stride & size:\n");
	// for(int i=0; i<RETINA_FPN_LAYER_NUM; i++){
	// 	pstSoftParam->image_stride[i] = image_stride[i];
	// 	pstSoftParam->anchor_size[i] = anchor_size[i];
	// 	// printf("%dth image stride: %d   anchor size: %d\n", i, pstSoftParam->image_stride[i], pstSoftParam->anchor_size[i]);
	// }
	HI_FLOAT anchor_scale[3] = {1, 1.26f, 1.587f};
	HI_FLOAT anchor_rate[3] = {0.5f, 1, 2};
    // printf("\nget anchor scale & rate:\n");
	// for(int i=0; i<3; i++){
	// 	pstSoftParam->anchor_scale[i] = anchor_scale[i];
	// 	pstSoftParam->anchor_rate[i] = anchor_rate[i];
	// 	// printf("%dth anchor scale: %.3f   anchor rate: %.3f\n", i, pstSoftParam->anchor_scale[i], pstSoftParam->anchor_rate[i]);
	// }

	// ***************       get NNIE blob       ***************
	for (HI_U32 i = 0; i < RETINA_FPN_LAYER_NUM * 2; i++) {
		if(i < RETINA_FPN_LAYER_NUM)
			pstSoftParam->detection_input_data[i] = (HI_S32*)pstDetParam->astSegData[0].astDst[i].u64VirAddr; // loc
		else
			pstSoftParam->softmax_input_data[i - RETINA_FPN_LAYER_NUM] = (HI_S32*)pstDetParam->astSegData[0].astDst[i].u64VirAddr; // conf
	}

	// ***************       get blob shape       ***************
    // printf("\nget featrue shape:\n");
	for (HI_U32 i = 0; i < RETINA_FPN_LAYER_NUM; i++){
		pstSoftParam->feature_width[i] = pstDetParam->astSegData[0].astDst[i].unShape.stWhc.u32Width;
		pstSoftParam->feature_height[i] = pstDetParam->astSegData[0].astDst[i].unShape.stWhc.u32Height;
		pstSoftParam->feature_channel_loc[i] = pstDetParam->astSegData[0].astDst[i].unShape.stWhc.u32Chn;
		pstSoftParam->feature_channel_conf[i] = pstDetParam->astSegData[0].astDst[i+RETINA_FPN_LAYER_NUM].unShape.stWhc.u32Chn;
		pstSoftParam->feature_stride[i] = pstDetParam->astSegData[0].astDst[i].u32Stride;
		// printf("%dth feature shape: w: %d   h: %d   c_loc: %d   c_conf: %d   s: %d\n", i, pstSoftParam->feature_width[i], pstSoftParam->feature_height[i], 
        //         pstSoftParam->feature_channel_loc[i], pstSoftParam->feature_channel_conf[i], pstSoftParam->feature_stride[i]);
	}

    pstSoftParam->total_detection_num = 0;
	// ***************       得分筛选 + 坐标解算       ***************
    // printf("\nget all bbox:\n");
	for (HI_U32 i = 0; i < RETINA_FPN_LAYER_NUM; i++){
		// printf("i: %d\n", i);
		HI_S32* ps32InputData = (HI_S32*)pstSoftParam->softmax_input_data[i];
		HI_S32* ps32InputLoc = (HI_S32*)pstSoftParam->detection_input_data[i];
		for(HI_U32 h=0; h<pstSoftParam->feature_height[i]; h++){ // h
//			cout<<" h: "<< h <<endl;
			for(HI_U32 w=0; w<pstSoftParam->feature_width[i]; w++){ // w
//				cout<<"  w: "<<w<<endl;
				for(HI_U32 anchor=0; anchor<RETINA_PER_ANCHOR_NUM; anchor++){ // anchor
//					cout<<"   anchor: "<<anchor<<endl;
					HI_FLOAT score = -9999;
					HI_U32 dclass = 0;
					for(HI_U32 c=0; c<RETINA_LABEL_NUM; c++){
						HI_U32 index = (c+anchor*RETINA_LABEL_NUM)*pstSoftParam->feature_stride[i]*pstSoftParam->feature_height[i]/4 +
									   h*pstSoftParam->feature_stride[i]/4 + w;
						HI_S32* InputSingleData = ps32InputData + index;
						HI_FLOAT confident = InputSingleData[0]*1.0f / SAMPLE_SVP_NNIE_QUANT_BASE;
						// cout<<"    score: "<<confident<< "   ps32InputData: " << ps32InputData
						// 	<< "   InputSingleData: " << InputSingleData << "   index: " << index << endl;
						if(confident > score){
							score = confident;
							dclass = c;
						}
					}

					// get bbox and save like [score class xyxh]
					if(score > RETINA_CLASS_THRESHOLD){
						HI_U32 rate_id = anchor / 3;
						HI_U32 scale_id = anchor % 3;

						HI_FLOAT anchor_w = anchor_scale[scale_id] * anchor_size[i] / sqrt(anchor_rate[rate_id]);
						HI_FLOAT anchor_h = anchor_scale[scale_id] * anchor_size[i] * sqrt(anchor_rate[rate_id]);
						HI_FLOAT anchor_x = image_stride[i] * (w + 0.5f);
						HI_FLOAT anchor_y = image_stride[i] * (h + 0.5f);

                        // printf("  w:%d  h:%d  anchor:%d  score:%.2f  conf:%d", w, h, anchor, score);
						// cout << "     w: " << w << "   h: " << h << "   anchor: " << anchor
						// 	 << "   score: " << score << "   conf: " << dclass <<endl;
						HI_FLOAT delta[RETINA_LOC_NUM]; // xywh
						for(HI_U32 l=0; l<RETINA_LOC_NUM; l++){
							HI_U32 index = (l+anchor*RETINA_LOC_NUM)*pstSoftParam->feature_stride[i]
									     * pstSoftParam->feature_height[i]/4
										 + h*pstSoftParam->feature_stride[i]/4 + w;
							HI_S32* InputSingleLoc = ps32InputLoc + index;
							delta[l] = InputSingleLoc[0]*1.0f / SAMPLE_SVP_NNIE_QUANT_BASE;
						}
						delta[0] *= 0.1f;
						delta[1] *= 0.1f;
						delta[2] *= 0.2f;
						delta[3] *= 0.2f;

						HI_FLOAT bbox_x = anchor_x + delta[0] * anchor_w;
						HI_FLOAT bbox_y = anchor_y + delta[1] * anchor_h;
						HI_FLOAT bbox_w = exp(delta[2]) * anchor_w;
						HI_FLOAT bbox_h = exp(delta[3]) * anchor_h;

						// printf("     w: %d   h: %d   anchor: %d   score: %.3f   conf: %d   num: %d   bx: %.3f   by: %.3f   bw: %.3f   bh: %.3f\n", 
                        //         w, h, anchor, score, dclass, pstSoftParam->total_detection_num, bbox_x, bbox_y, bbox_w, bbox_h);

						if(pstSoftParam->total_detection_num >= SAVE_RESULT_NUM_MAX){
							printf("Warning! total_detection_num out of memory\n");
							break;
						}

                        HI_FLOAT* ps32BboxResultTmp = (HI_FLOAT*)pstSoftParam->ps32ResultMem.u64VirAddr;
						ps32BboxResultTmp[pstSoftParam->total_detection_num*6] = score;
						ps32BboxResultTmp[pstSoftParam->total_detection_num*6 + 1] = dclass;
						ps32BboxResultTmp[pstSoftParam->total_detection_num*6 + 2] = myClip(bbox_x - bbox_w/2); // x
						ps32BboxResultTmp[pstSoftParam->total_detection_num*6 + 3] = myClip(bbox_y - bbox_h/2); // y
						ps32BboxResultTmp[pstSoftParam->total_detection_num*6 + 4] = myClip(bbox_x + bbox_w/2); // x
						ps32BboxResultTmp[pstSoftParam->total_detection_num*6 + 5] = myClip(bbox_y + bbox_h/2); // y

						pstSoftParam->total_detection_num ++;
					}
				} // anchor
			} // w
		} // h
	} // stage
    // printf("\ndetect num: %d    before sort score: \n", pstSoftParam->total_detection_num);
	// for(HI_U32 ii=0; ii<pstSoftParam->total_detection_num; ++ii)
	// 	printf("ii: %d   score: %.3f\n", ii, ((HI_FLOAT*)pstSoftParam->ps32ResultMem.u64VirAddr)[ii*6]);
    
    // ***************       sort by scores       ***************
	QuickSort_6((HI_FLOAT *)pstSoftParam->ps32ResultMem.u64VirAddr, 0, pstSoftParam->total_detection_num*6-6); // sort
	// printf("\nhas %d detect result, score sort:\n", pstSoftParam->total_detection_num);
	// for(HI_U32 ii=0; ii<pstSoftParam->total_detection_num; ++ii)
	// 	printf("%.3f\n", ((HI_FLOAT*)pstSoftParam->ps32ResultMem.u64VirAddr)[ii*6]);

	pstSoftParam->final_detection_num = 0;
	// printf("\nstart nms:\n");
	// ***************       NMS for each class       ***************
    HI_FLOAT* all_result = (HI_FLOAT*)pstSoftParam->ps32ResultMem.u64VirAddr; // [score class x1 y1 x2 y2]
    HI_BOOL is_suppression[SAVE_RESULT_NUM_MAX] = {0}; // 是否被抑制
	for(HI_S32 ii=0; ii<RETINA_LABEL_NUM; ii++){ // for every class
		// printf(" ii class: %d\n", ii);
		for(HI_U32 jj=0; jj<pstSoftParam->total_detection_num; ++jj){
			if(all_result[jj*6 + 1] != ii || is_suppression[jj] == HI_TRUE) // 如果不是这个类别 或 被抑制， 则跳过
				continue;

			// printf("  jj: %d\n", jj);
			is_suppression[jj] = HI_TRUE;
			for(HI_U32 ll=0; ll<6; ++ll){
                ((HI_FLOAT*)pstSoftParam->ps32FinalBbox.u64VirAddr)[pstSoftParam->final_detection_num*6 + ll]
                                            = all_result[jj*6 + ll];
			}
			pstSoftParam->final_detection_num ++;

			for(HI_U32 kk=jj+1; kk<pstSoftParam->total_detection_num; ++kk){
				// printf("   kk: %d\n", kk);
				if(all_result[kk*6 + 1] != ii || is_suppression[kk] == HI_TRUE)
					continue;
				// printf(" ii: %d jj: %d kk: %d\n", ii, jj, kk);

				HI_FLOAT xj1 = all_result[jj*6 + 2];
				HI_FLOAT yj1 = all_result[jj*6 + 3];
				HI_FLOAT xj2 = all_result[jj*6 + 4];
				HI_FLOAT yj2 = all_result[jj*6 + 5];

				HI_FLOAT xk1 = all_result[kk*6 + 2];
				HI_FLOAT yk1 = all_result[kk*6 + 3];
				HI_FLOAT xk2 = all_result[kk*6 + 4];
				HI_FLOAT yk2 = all_result[kk*6 + 5];

				HI_FLOAT overlap_top_left_x = xj1 > xk1 ? xj1 : xk1; // max
				HI_FLOAT overlap_top_left_y = yj1 > yk1 ? yj1 : yk1;
				HI_FLOAT overlap_bot_right_x = xj2 > xk2 ? xk2 : xj2; // min
				HI_FLOAT overlap_bot_right_y = yj2 > yk2 ? yk2 : yj2;
				HI_FLOAT overlap_area;
				if((overlap_bot_right_x - overlap_top_left_x < 0) || (overlap_bot_right_y - overlap_top_left_y < 0))
					overlap_area = 0;
				else
					overlap_area = (overlap_bot_right_x - overlap_top_left_x) * (overlap_bot_right_y - overlap_top_left_y);

				HI_FLOAT union_area = (yk2 - yk1)*(xk2 - xk1) + (yj2 - yj1)*(xj2 - xj1) - overlap_area;
				HI_FLOAT IoU = overlap_area / union_area+0.0001f;

				// printf("  over: %.3f  union: %.3f  IoU: %.3f", overlap_area, union_area, IoU);
				if(IoU > NMS_IOU_THRESHOLD){
					is_suppression[kk] = HI_TRUE;
					// printf("   is suppression\n");
				}
				// else
				// 	printf("\n");
			}
		}
	}

    return s32Ret;
}




/* *****************************************************

jpg -> vo 子代码

******************************************************* */
// roi转到rect
HI_S32 My_MblSsd_RoiToRect(HI_U32 u32SrcWidth, HI_U32 u32SrcHeight, // 检测图像 大小
    HI_U32 u32DstWidth,HI_U32 u32DstHeight) // 显示图像 大小
{
    // 类别个数不能超过30 (同时 每个类别roi数不能大于50)
    SAMPLE_SVP_CHECK_EXPR_RET(RETINA_LABEL_NUM > SAMPLE_SVP_NNIE_MAX_CLASS_NUM ,HI_ERR_SVP_NNIE_ILLEGAL_PARAM, SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error(%#x),u32ClassNum(%u) must be less than or equal %u to!\n", HI_ERR_SVP_NNIE_ILLEGAL_PARAM, RETINA_LABEL_NUM, SAMPLE_SVP_NNIE_MAX_CLASS_NUM);

    // 输出   只存储坐标,4个点,每个点x/y坐标,共8个值
    SAMPLE_SVP_NNIE_RECT_ARRAY_S* pstRect = &stSoftParam.stRect;
    pstRect->u32TotalNum = stSoftParam.final_detection_num; // 总roi个数
    pstRect->u32ClsNum = RETINA_LABEL_NUM; // 类别数

    for(HI_U32 ii=0; ii<RETINA_LABEL_NUM; ++ii)
        pstRect->au32RoiNum[ii] = 0;// 每个类别roi个数

    HI_FLOAT* final_result_tmp = (HI_FLOAT*)stSoftParam.ps32FinalBbox.u64VirAddr;
    for(HI_U32 ii=0; ii<stSoftParam.final_detection_num; ++ii){
        if(final_result_tmp[ii*6] < SHOW_CLASS_THRESHOLD)
            continue;
		printf("   score: %.3f   class: %d   xyxy: %.3f  %.3f  %.3f  %.3f\n", 
            final_result_tmp[ii*6], (HI_S32)final_result_tmp[ii*6+1], final_result_tmp[ii*6+2], 
            final_result_tmp[ii*6+3], final_result_tmp[ii*6+4], final_result_tmp[ii*6+5]);

        HI_S32 class_id = (HI_S32)final_result_tmp[ii*6+1];
        HI_S32 u32RoiNumTmp = pstRect->au32RoiNum[class_id];

        // 左上角
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[0].s32X = (HI_U32)(final_result_tmp[ii*6+2] / u32SrcWidth * u32DstWidth) & (~1) ; // 全部变成偶数?
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[0].s32Y = (HI_U32)(final_result_tmp[ii*6+3] / u32SrcHeight * u32DstHeight) & (~1);

        // 右上角
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[1].s32X = (HI_U32)(final_result_tmp[ii*6+4] / u32SrcWidth * u32DstWidth) & (~1);
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[1].s32Y = pstRect->astRect[class_id][u32RoiNumTmp].astPoint[0].s32Y;

        // 右下角
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[2].s32X = pstRect->astRect[class_id][u32RoiNumTmp].astPoint[1].s32X;
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[2].s32Y = (HI_U32)(final_result_tmp[ii*6+5] / u32SrcHeight * u32DstHeight) & (~1);

        // 左下角
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[3].s32X =  pstRect->astRect[class_id][u32RoiNumTmp].astPoint[0].s32X;
        pstRect->astRect[class_id][u32RoiNumTmp].astPoint[3].s32Y =  pstRect->astRect[class_id][u32RoiNumTmp].astPoint[2].s32Y;

        pstRect->au32RoiNum[class_id] ++;
    }

    return HI_SUCCESS;
}


// nnie → 软件处理 → 得到rect
HI_S32 My_MblSsd_Proc(VIDEO_FRAME_INFO_S* pstExtFrmInfo, // 额外帧信息
    HI_U32 u32BaseWidth, HI_U32 u32BaseHeight) // 基础帧长、宽
{
    HI_S32 s32Ret = HI_FAILURE;
    SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S stInputDataIdx = {0};

    stInputDataIdx.u32SegIdx = 0;
    stInputDataIdx.u32NodeIdx = 0;

    {
        /*******    已实现yuv420sp 2 bgr    ******/
        HI_U8 i_circul; 
        HI_BOOL bFinish;
        HI_BOOL bBlock = HI_TRUE;
        IVE_HANDLE hIveHandle;
        HI_BOOL bInstant = HI_TRUE;

        // IVE_CSC_MODE_PIC_BT709_YUV2RGB  IVE_CSC_MODE_PIC_BT601_YUV2RGB  IVE_CSC_MODE_VIDEO_BT709_YUV2RGB   IVE_CSC_MODE_VIDEO_BT601_YUV2RGB
        IVE_CSC_CTRL_S stCscCtrl;
        stCscCtrl.enMode = IVE_CSC_MODE_PIC_BT601_YUV2RGB; 

        // 填充yuv图片
        IVE_SRC_IMAGE_S stSrcData;
        stSrcData.enType = IVE_IMAGE_TYPE_YUV420SP;
        for(i_circul = 0; i_circul < 3; i_circul++)
        {
            stSrcData.au64PhyAddr[i_circul] = pstExtFrmInfo->stVFrame.u64PhyAddr[i_circul];
            stSrcData.au64VirAddr[i_circul] = pstExtFrmInfo->stVFrame.u64VirAddr[i_circul];
            stSrcData.au32Stride[i_circul] = pstExtFrmInfo->stVFrame.u32Stride[i_circul];
            // printf("%dth src stride: %d\n", i_circul, stSrcData.au32Stride[i_circul]);
        }
        stSrcData.u32Width = pstExtFrmInfo->stVFrame.u32Width;
        stSrcData.u32Height = pstExtFrmInfo->stVFrame.u32Height;
        // printf("yuv  w:%d h:%d\n", stSrcData.u32Width, stSrcData.u32Height);

        // 创建输出图像的参数、类型
        IVE_DST_IMAGE_S stDstData; // csc只支持这个类型的参数输入，因此将要存图像的地址，赋给这个参数
        stDstData.enType = IVE_IMAGE_TYPE_U8C3_PLANAR;
        for(i_circul = 0; i_circul < 3; i_circul++){
            stDstData.au64PhyAddr[i_circul] = s_stRetinaNnieParam.astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64PhyAddr 
                + i_circul*SAMPLE_SVP_NNIE_ALIGN16(RETINA_INPUT_SIZE)*RETINA_INPUT_SIZE; // 跨度304 高300 一个通道，91200
            stDstData.au64VirAddr[i_circul] = s_stRetinaNnieParam.astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u64VirAddr 
                + i_circul*SAMPLE_SVP_NNIE_ALIGN16(RETINA_INPUT_SIZE)*RETINA_INPUT_SIZE;
            stDstData.au32Stride[i_circul] = s_stRetinaNnieParam.astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].u32Stride;
        }
        stDstData.u32Width = s_stRetinaNnieParam.astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].unShape.stWhc.u32Width;
        stDstData.u32Height = s_stRetinaNnieParam.astSegData[stInputDataIdx.u32SegIdx].astSrc[stInputDataIdx.u32NodeIdx].unShape.stWhc.u32Height;
        // printf("bgr  w:%d h:%d\n", stDstData.u32Width, stDstData.u32Height); // pelee是这里出现了问题 转换图像设置为300，应该为304

        // yuv 转换 成bgr 开始
        s32Ret = HI_MPI_IVE_CSC(&hIveHandle, &stSrcData, &stDstData, &stCscCtrl, bInstant);
        if(HI_SUCCESS != s32Ret){   
            // printf("Error!! IVE_CSC , code:%d\n", s32Ret);
            // return s32Ret;
            SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
                "Error!! IVE_CSC failed!\n");
        }

        // 等待转换完成
        if (HI_TRUE == bInstant){
            s32Ret = HI_MPI_IVE_Query(hIveHandle,&bFinish,bBlock);
            while(HI_ERR_IVE_QUERY_TIMEOUT == s32Ret)
            {
                usleep(100);
                s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
            }

            if(HI_SUCCESS != s32Ret)
            {
                printf("Error!! IVE_Query , code:%d\n", s32Ret);
                return s32Ret;
            }
        }
    } 
    PrintTime("yuv2bgr", 0, HI_TRUE, 1);
    
    // ***************       NNIE forward       ***************
    SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S stProcSegIdx = {0};
    stProcSegIdx.u32SegIdx = 0;
    s32Ret = my_NNIE_Forward(&s_stRetinaNnieParam, &stInputDataIdx, &stProcSegIdx, HI_TRUE);
    SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret, SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_SVP_NNIE_Forward failed!\n");
    PrintTime("NNIE forward", 0, HI_TRUE, 1);

    // ***************       soft forward       ***************
    s32Ret = Retinanet_Soft(&s_stRetinaNnieParam, &stSoftParam);
    PrintTime("soft forward", 0, HI_TRUE, 1);
    

    // ***************       roi to rect       ***************
    s32Ret = My_MblSsd_RoiToRect(pstExtFrmInfo->stVFrame.u32Width, pstExtFrmInfo->stVFrame.u32Height, u32BaseWidth, u32BaseHeight);
    SAMPLE_SVP_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error(%#x),SAMPLE_SVP_NNIE_RoiToRect failed!\n",s32Ret);

    return s32Ret;
}

// vdec接收jpg图像
HI_VOID * mySAMPLE_COMM_VDEC_SendStream(HI_VOID *pArgs)
{
    SAMPLE_SVP_TRACE_INFO("start pthread!\n");
    myRETINA_THREAD *apstmyRetinaThreadParam = (myRETINA_THREAD *)pArgs; // 函数输入
    VDEC_THREAD_PARAM_S *pstVdecThreadParam = apstmyRetinaThreadParam->pstVdecSend;

    prctl(PR_SET_NAME, "VideoSendStream", 0,0,0); // 把参数arg2作为调用进程的经常名字
    HI_S32 s32Ret,  i;

    // JPG相关
    HI_CHAR cStreamFile[1023]; // 用来存储jpg的名字
    FILE *fpStrm = NULL; // JPG文件指针
    HI_U8 *pu8Buf = malloc(pstVdecThreadParam->s32MinBufSize); // 存储JPG原始数据
    if(pu8Buf == NULL){
        SAMPLE_PRT("chn %d can't alloc %d in send stream thread!\n", pstVdecThreadParam->s32ChnId, pstVdecThreadParam->s32MinBufSize);
        fclose(fpStrm);
        return (HI_VOID *)(HI_FAILURE);
    }
    fflush(stdout); // 标准输出流的清理 打印数据到屏幕上


    VDEC_STREAM_S stStream; // 发送到vdec的数据流
 
    HI_S32 s32ReadLen = 0; // 读取文件的长度
    HI_U32 len_jpg_name; // jpg图片名字长度

    // ls img_jpg | sed "s:^:./retinanet/img_jpg/:" >> jpg_list.txt
    FILE *fp_jpg = fopen("./retinanet/jpg_list.txt", "r");
    
    while(1)
    {
        PrintTime(" ", 1, 0, 0);
        
        // 控制位 退出/暂停1s
        if (pstVdecThreadParam->eThreadCtrl == THREAD_CTRL_STOP)
        {
            break;
        }
        else if (pstVdecThreadParam->eThreadCtrl == THREAD_CTRL_PAUSE)
        {
            sleep(1);
            continue;
        }

        // 获取图片路径
        if(fgets(cStreamFile, 1023, fp_jpg) == NULL)
            continue;
        len_jpg_name = strlen(cStreamFile); // 修正jpg的名字
        cStreamFile[len_jpg_name - 1] = '\0';
        printf("\nimg name: %s\n", cStreamFile);

        // 标志位/参数
        HI_BOOL bFindStart   = HI_FALSE; // 是否找到图片的 首尾指针
        // HI_BOOL bFindEnd     = HI_FALSE;
        HI_U32 u32Start = 0; // 数据首指针位置  防止有多余数据,严格读取jpg格式


        // 打开JPG
        if(cStreamFile != 0)
        {
            fpStrm = fopen(cStreamFile, "rb");
            if(fpStrm == NULL)
            {
                SAMPLE_PRT("chn %d can't open file %s in send stream thread!\n", pstVdecThreadParam->s32ChnId, cStreamFile);
                return (HI_VOID *)(HI_FAILURE);
            }
        }

        // 读取jpg数据 到pu8Buf
        s32ReadLen = fread(pu8Buf, 1, pstVdecThreadParam->s32MinBufSize, fpStrm); // 从数据流读取 1size bufsize个数据到 pu8Buf

        // 检查JPG格式
        if (pstVdecThreadParam->enType == PT_MJPEG || pstVdecThreadParam->enType == PT_JPEG) // 这个模式
        { 
            // 找头指针
            for (i=0; i<s32ReadLen-1; i++)
            {
                if (pu8Buf[i] == 0xFF && pu8Buf[i+1] == 0xD8) // 1111 1111 1101 1000
                {
                    u32Start = i; // 存储首指针位置
                    // printf("\n%d\n", i); // 0
                    bFindStart = HI_TRUE;
                    i = i + 2;
                    break;
                }
            }

            // 统计
            for (; i<s32ReadLen-3; i++) // 整个编码数据是分段存储的,中间有标志位FFEx,随后跟着的是这段存储大小
            {
                if ((pu8Buf[i] == 0xFF) && (pu8Buf[i+1]& 0xF0) == 0xE0) // 1111 1111 1110 xxxx
                {
                    // printf("%d\n", i); // 2 20 6532 14780 18503
                    HI_U32 u32Len = (pu8Buf[i+2]<<8) + pu8Buf[i+3]; // FFEx后面接着的16位,对应长度,需要跳转这么多
                    i += 1 + u32Len;
                }
                else
                {
                    break;
                }
            }

            for (; i<s32ReadLen-1; i++) // 找尾指针
            {
                if (pu8Buf[i] == 0xFF && pu8Buf[i+1] == 0xD9) // 1111 1111 1101 1001
                {
                    // printf("%d\n", i); // 394128
                    // bFindEnd = HI_TRUE;
                    break;
                }
            }
            s32ReadLen = i+2; // 跳过尾字节

            if (bFindStart == HI_FALSE) // 验证是否找到首指针
            {
                SAMPLE_PRT("chn %d can not find JPEG start code!s32ReadLen %d.!\n",
                    pstVdecThreadParam->s32ChnId, s32ReadLen);
            }
        }

        stStream.u64PTS       = 0; // 码流包的时间戳us
        stStream.pu8Addr      = pu8Buf + u32Start; // 有用数据首指针位置
        stStream.u32Len       = s32ReadLen; // 数据总长度 (包括无用的数据)
        stStream.bEndOfFrame  = HI_TRUE; // 当前帧是否结束
        stStream.bEndOfStream = HI_FALSE; // 是否发送完所有码流 false表示要继续发送 true表示可以开始解码了
        stStream.bDisplay     = 1;

SendAgain: // 向视频解码通道发送码流数据
        s32Ret=HI_MPI_VDEC_SendStream(pstVdecThreadParam->s32ChnId, &stStream, pstVdecThreadParam->s32MilliSec); // 0非阻塞
        if( (HI_SUCCESS != s32Ret) && (THREAD_CTRL_START == pstVdecThreadParam->eThreadCtrl) ) // 发送失败,且控制flag为运行
        {
            usleep(pstVdecThreadParam->s32IntervalTime);
            goto SendAgain;
        }
        
        // JPG文件关闭
        fclose(fpStrm); 
        fpStrm = NULL;
        
        /************************************************
        step: 检测
        *************************************************/
        {
            /********    获取图片    *********/
            HI_S32 s32VpssGrp = 0; // vpss group号 
            HI_S32 as32VpssChn[] = {VPSS_CHN0, VPSS_CHN1}; // vpss 通道号
            HI_S32 s32MilliSec = 20; // 获取图像等待时间
            VIDEO_FRAME_INFO_S stShowInfo, stDetectInfo; // 显示图像 & 检测图像
    
            // get显示图片   1920x1080
            s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, as32VpssChn[1], &stShowInfo, s32MilliSec);
            if(HI_SUCCESS != s32Ret){
                SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_GetChnFrame failed, VPSS_GRP(%d), VPSS_CHN(%d)!\n",
                    s32Ret,s32VpssGrp, as32VpssChn[1]);
                goto SHOW_RELEASE;
            }

            // get检测图片   300x300
            s32Ret = HI_MPI_VPSS_GetChnFrame(s32VpssGrp, as32VpssChn[0], &stDetectInfo, s32MilliSec);
            if(HI_SUCCESS != s32Ret){
                SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_GetChnFrame failed, VPSS_GRP(%d), VPSS_CHN(%d)!\n",
                    s32Ret,s32VpssGrp, as32VpssChn[0]);
                goto DETECT_RELEASE;
            }
            PrintTime("get yuv", 0, HI_TRUE, 1);

            /********    检测    *********/
            s32Ret = My_MblSsd_Proc(&stDetectInfo, stShowInfo.stVFrame.u32Width, stShowInfo.stVFrame.u32Height);
            SAMPLE_CHECK_EXPR_GOTO(HI_SUCCESS!=s32Ret, DETECT_RELEASE,
                "Error(%#x),SAMPLE_SVP_NNIE_Rfcn_Proc failed!\n", s32Ret);

            /********    IVE画框    *********/
            s32Ret = SAMPLE_COMM_SVP_NNIE_FillRect(&stShowInfo, &(stSoftParam.stRect), 0x0000FF00); // 输出图片信息  bbox  框颜色
            SAMPLE_CHECK_EXPR_GOTO(HI_SUCCESS!=s32Ret, DETECT_RELEASE, "SAMPLE_COMM_SVP_NNIE_FillRect failed, Error(%#x)!\n", s32Ret);

            /********    显示    *********/
            VO_LAYER voLayer = 0; // 视频输出视频层号 
            VO_CHN voChn = 0; // 视频输出通道的通道号
 
            // 发送到VO
            s32Ret = HI_MPI_VO_SendFrame(voLayer, voChn, &stShowInfo, s32MilliSec);
            SAMPLE_CHECK_EXPR_GOTO(HI_SUCCESS!=s32Ret, DETECT_RELEASE, "HI_MPI_VO_SendFrame failed, Error(%#x)!\n", s32Ret);
            PrintTime("hdmi output", HI_TRUE, HI_TRUE, 1);

            // 释放检测图片
        DETECT_RELEASE:
            s32Ret = HI_MPI_VPSS_ReleaseChnFrame(s32VpssGrp, as32VpssChn[0], &stDetectInfo);
            if (HI_SUCCESS != s32Ret){
                SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n",
                    s32Ret,s32VpssGrp, as32VpssChn[0]);
            }

            // 释放显示图片
        SHOW_RELEASE:
            s32Ret = HI_MPI_VPSS_ReleaseChnFrame(s32VpssGrp, as32VpssChn[1], &stShowInfo);
            if (HI_SUCCESS != s32Ret){
                SAMPLE_PRT("Error(%#x),HI_MPI_VPSS_ReleaseChnFrame failed,Grp(%d) chn(%d)!\n",
                    s32Ret,s32VpssGrp, as32VpssChn[1]);
            }

            // usleep(2000000);
        }

    }


    // 将最后的数据进行解码
    memset(&stStream, 0, sizeof(VDEC_STREAM_S) );
    stStream.bEndOfStream = HI_TRUE; // 发送完所有码流
    HI_MPI_VDEC_SendStream(pstVdecThreadParam->s32ChnId, &stStream, -1); // 将最后一个数据流解码

    fflush(stdout); // 标准输出流的清理 打印数据到屏幕上

    if (pu8Buf != HI_NULL) // 存储JPG的内存释放
    {
        free(pu8Buf);
    }

    if(fpStrm != NULL) // JPG文件关闭
        fclose(fpStrm);

    return (HI_VOID *)HI_SUCCESS;
}

// 开启接收jpg图像的线程
HI_VOID mySAMPLE_COMM_VDEC_StartSendStream(HI_S32 s32ChnNum, pthread_t *pVdecThread, myRETINA_THREAD *pretina_thred_para)
{
    HI_S32  i;
    sleep(1); // 1000
    
    for(i=0; i<s32ChnNum; i++)
    {
        pVdecThread[i] = 0;
        pthread_create(&pVdecThread[i], 0, mySAMPLE_COMM_VDEC_SendStream, (HI_VOID *)pretina_thred_para);
    }
}


HI_VOID mySAMPLE_COMM_VDEC_StopSendStream(HI_S32 s32ChnNum, VDEC_THREAD_PARAM_S *pstVdecSend, pthread_t *pVdecThread)
{
    HI_S32  i;

    for(i=0; i<s32ChnNum; i++)
    {
        pstVdecSend[i].eThreadCtrl = THREAD_CTRL_STOP;
        HI_MPI_VDEC_StopRecvStream(i); // 解码器停止接收用户发送的码流
        if(0 != pVdecThread[i])
        {
            pthread_join(pVdecThread[i], HI_NULL);
            pVdecThread[i] = 0;
        }
    }
}








/* *****************************************************

本地读取一张bgr,
打印roi

******************************************************* */
void RetinaNet_aBGR_to_print(void)
{
    HI_CHAR *pcSrcFile = "./retinanet/img_bgr/000094.bgr";
    // "./data/nnie_image/rgb_planar/dog_bike_car_300x300.bgr";
    // HI_CHAR *pcModelName = "./retinanet/model/retina_inst.wk";
    HI_CHAR *pcModelName = "./retinanet/model/res_inst20.wk";

    HI_S32 s32Ret = HI_SUCCESS;
    clock_gettime(CLOCK_REALTIME, &start_time); // 开始计时

    // ***************       sys init       ***************
    SAMPLE_COMM_SVP_CheckSysInit();
    PrintTime("Sys init", 0, 0, 1);

    // ***************       load wk       ***************
    // SAMPLE_SVP_TRACE_INFO("Load retinanet model!\n");
    s32Ret = SAMPLE_COMM_SVP_NNIE_LoadModel(pcModelName, &s_stRetinaModel);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_LoadModel failed!\n");
    PrintTime("load wk", 0, 0, 1);
    
    // ***************       NNIE init       ***************
    // SAMPLE_SVP_TRACE_INFO("retina NNIE parameter init!\n");
    SAMPLE_SVP_NNIE_CFG_S   stNnieCfg = {0};
    stNnieCfg.pszPic= pcSrcFile;
    stNnieCfg.u32MaxInputNum = 1; // max input image num in each batch
    stNnieCfg.u32MaxRoiNum = 0;
    stNnieCfg.aenNnieCoreId[0] = SVP_NNIE_ID_0; // set NNIE core
    s_stRetinaNnieParam.pstModel = &s_stRetinaModel.stModel;
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamInit(&stNnieCfg, &s_stRetinaNnieParam);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error(%#x),SAMPLE_COMM_SVP_NNIE_ParamInit failed!\n",s32Ret);
    PrintTime("NNIE init", 0, 0, 1);

    // ***************       soft init       ***************
    {
        HI_U64 u64PhyAddr = 0;
        HI_U8* pu8VirAddr = NULL;

        stSoftParam.ps32ResultMem.u32Size = SAMPLE_SVP_NNIE_ALIGN16(SAVE_RESULT_NUM_MAX * 6 * sizeof(HI_FLOAT));
        stSoftParam.ps32FinalBbox.u32Size = SAMPLE_SVP_NNIE_ALIGN16(DETECTION_RESULT_NUM_MAX * 6 * sizeof(HI_FLOAT));
        HI_U32 u32TotalSize = stSoftParam.ps32ResultMem.u32Size + stSoftParam.ps32FinalBbox.u32Size;

        s32Ret = SAMPLE_COMM_SVP_MallocCached("SAMPLE_SSD_INIT", NULL, (HI_U64*)&u64PhyAddr, (void**)&pu8VirAddr, u32TotalSize);
        SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error,Malloc memory failed!\n");
        memset(pu8VirAddr, 0, u32TotalSize);
        SAMPLE_COMM_SVP_FlushCache(u64PhyAddr, (void*)pu8VirAddr, u32TotalSize);

        stSoftParam.ps32ResultMem.u64PhyAddr = u64PhyAddr;
        stSoftParam.ps32ResultMem.u64VirAddr = (HI_U64)(pu8VirAddr);

        stSoftParam.ps32FinalBbox.u64PhyAddr = u64PhyAddr + stSoftParam.ps32ResultMem.u32Size;
        stSoftParam.ps32FinalBbox.u64VirAddr = (HI_U64)(pu8VirAddr + stSoftParam.ps32ResultMem.u32Size);
    }
    PrintTime("soft init", 0, 0, 1);

    // ***************       fill bgr       ***************
    // SAMPLE_SVP_TRACE_INFO("Fill img!\n");
    SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S stInputDataIdx = {0};
    stInputDataIdx.u32SegIdx = 0;
    stInputDataIdx.u32NodeIdx = 0;
    s32Ret = my_NNIE_FillSrcData(&stNnieCfg,&s_stRetinaNnieParam,&stInputDataIdx);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error,SAMPLE_SVP_NNIE_FillSrcData failed!\n");
    PrintTime("fill bgr", 0, HI_TRUE, 1);

    // ***************       NNIE forward       ***************
    // SAMPLE_SVP_TRACE_INFO("NNIE Forward!\n");
    SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S stProcSegIdx = {0};
    stProcSegIdx.u32SegIdx = 0;
    s32Ret = my_NNIE_Forward(&s_stRetinaNnieParam, &stInputDataIdx, &stProcSegIdx, HI_TRUE);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_SVP_NNIE_Forward failed!\n");
    PrintTime("NNIE forward", 0, HI_TRUE, 1);

    // printf("each blob shape:\n");
    // for(int ii=0; ii<RETINA_FPN_LAYER_NUM*2; ++ii)
    //     printf("%dth blob's w:%d h:%d c:%d s:%d\n", ii, s_stRetinaNnieParam.astSegData[0].astDst[ii].unShape.stWhc.u32Width, 
    //             s_stRetinaNnieParam.astSegData[0].astDst[ii].unShape.stWhc.u32Height, s_stRetinaNnieParam.astSegData[0].astDst[ii].unShape.stWhc.u32Chn, 
    //             s_stRetinaNnieParam.astSegData[0].astDst[ii].u32Stride);

    // ***************       soft forward       ***************
    s32Ret = Retinanet_Soft(&s_stRetinaNnieParam, &stSoftParam);
    PrintTime("soft forward", HI_TRUE, HI_TRUE, 1);
    

    // ***************       print infor       ***************
    HI_FLOAT* final_result_tmp = (HI_FLOAT*)stSoftParam.ps32FinalBbox.u64VirAddr;
	for(HI_U32 ii=0; ii<stSoftParam.final_detection_num; ++ii){
		printf("   score: %.3f   class: %d   xyxy: %.3f  %.3f  %.3f  %.3f\n", 
            final_result_tmp[ii*6], (HI_S32)final_result_tmp[ii*6+1], final_result_tmp[ii*6+2], 
            final_result_tmp[ii*6+3], final_result_tmp[ii*6+4], final_result_tmp[ii*6+5]);
    }
    PrintTime("not print", 0, 0, 0);


Retina_FAIL_0:
    Retinanet_HandleSig();
}


/* *****************************************************

本地读取bgr list,
打印roi,
保存到txt中

******************************************************* */
void RetinaNet_BGR_to_List(){
    // ls img_bgr | sed "s:^:./retinanet/img_bgr/:" >> bgr_list.txt
    FILE *fp_list = fopen("./retinanet/bgr_list.txt", "r");// 判断list是否为空
    HI_CHAR *pcModelName = "./retinanet/model/mbl_20_inst.wk";
    // HI_CHAR *pcModelName = "./retinanet/model/res_inst20.wk";
    // HI_CHAR *pcModelName = "./retinanet/model/mbl_nobn_20_inst.wk";
    // HI_CHAR *pcModelName = "./retinanet/model/res_nobn_20_inst.wk";
    
    // sudo chown ccc bbox_result.txt
    stSoftParam.fp_result = fopen("./retinanet/bbox_result.txt", "w"); // 创建文件
    
    if(fp_list == NULL){ // list不能为空
        printf("no file\n"); 
        exit(1);
    }

    HI_S32 s32Ret = HI_SUCCESS;
    HI_CHAR pcSrcFile[1023]; // 每张bgr的名字
    HI_U32 len_image_name; // bgr名字的长度 

    clock_gettime(CLOCK_REALTIME, &start_time); // 开始计时

    // ***************       sys init       ***************
    SAMPLE_COMM_SVP_CheckSysInit();
    PrintTime("Sys init", 0, 0, 1);

    // ***************       load wk       ***************
    // SAMPLE_SVP_TRACE_INFO("Load retinanet model!\n");
    s32Ret = SAMPLE_COMM_SVP_NNIE_LoadModel(pcModelName,&s_stRetinaModel);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_LoadModel failed!\n");
    PrintTime("load wk", 0, 0, 1);
    
    // ***************       NNIE init       ***************
    // SAMPLE_SVP_TRACE_INFO("retina NNIE parameter init!\n");
    SAMPLE_SVP_NNIE_CFG_S   stNnieCfg = {0};
    stNnieCfg.u32MaxInputNum = 1; // max input image num in each batch
    stNnieCfg.u32MaxRoiNum = 0;
    stNnieCfg.aenNnieCoreId[0] = SVP_NNIE_ID_0; // set NNIE core
    s_stRetinaNnieParam.pstModel = &s_stRetinaModel.stModel;
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamInit(&stNnieCfg, &s_stRetinaNnieParam);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error(%#x),SAMPLE_COMM_SVP_NNIE_ParamInit failed!\n",s32Ret);
    PrintTime("NNIE init", 0, 0, 1);

    // ***************       soft init       ***************
    {
        HI_U64 u64PhyAddr = 0;
        HI_U8* pu8VirAddr = NULL;

        stSoftParam.ps32ResultMem.u32Size = SAMPLE_SVP_NNIE_ALIGN16(SAVE_RESULT_NUM_MAX * 6 * sizeof(HI_FLOAT));
        stSoftParam.ps32FinalBbox.u32Size = SAMPLE_SVP_NNIE_ALIGN16(DETECTION_RESULT_NUM_MAX * 6 * sizeof(HI_FLOAT));
        HI_U32 u32TotalSize = stSoftParam.ps32ResultMem.u32Size + stSoftParam.ps32FinalBbox.u32Size;

        s32Ret = SAMPLE_COMM_SVP_MallocCached("SAMPLE_SSD_INIT", NULL, (HI_U64*)&u64PhyAddr, (void**)&pu8VirAddr, u32TotalSize);
        SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error,Malloc memory failed!\n");
        memset(pu8VirAddr, 0, u32TotalSize);
        SAMPLE_COMM_SVP_FlushCache(u64PhyAddr, (void*)pu8VirAddr, u32TotalSize);

        stSoftParam.ps32ResultMem.u64PhyAddr = u64PhyAddr;
        stSoftParam.ps32ResultMem.u64VirAddr = (HI_U64)(pu8VirAddr);

        stSoftParam.ps32FinalBbox.u64PhyAddr = u64PhyAddr + stSoftParam.ps32ResultMem.u32Size;
        stSoftParam.ps32FinalBbox.u64VirAddr = (HI_U64)(pu8VirAddr + stSoftParam.ps32ResultMem.u32Size);
    }
    PrintTime("soft init", 0, 0, 1);

    SAMPLE_SVP_NNIE_INPUT_DATA_INDEX_S stInputDataIdx = {0}; // 哪个段？有哪些源节点？
    SAMPLE_SVP_NNIE_PROCESS_SEG_INDEX_S stProcSegIdx = {0};

    while(fgets(pcSrcFile, 1023, fp_list) != NULL){
        // 获取图片路径
        len_image_name = strlen(pcSrcFile);
        pcSrcFile[len_image_name - 1] = '\0';
        printf("\nimg name: %s\n", pcSrcFile);


        // ***************       fill bgr       ***************
        // SAMPLE_SVP_TRACE_INFO("Fill img!\n");
        stNnieCfg.pszPic= pcSrcFile; // 图片
        stInputDataIdx.u32SegIdx = 0; // 段id
        stInputDataIdx.u32NodeIdx = 0; // 源节点id
        s32Ret = my_NNIE_FillSrcData(&stNnieCfg,&s_stRetinaNnieParam,&stInputDataIdx);
        SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error,SAMPLE_SVP_NNIE_FillSrcData failed!\n");
        PrintTime("fill bgr", 0, HI_TRUE, 1);

        // ***************       NNIE forward       ***************
        // SAMPLE_SVP_TRACE_INFO("NNIE Forward!\n");
        stProcSegIdx.u32SegIdx = 0;
        s32Ret = my_NNIE_Forward(&s_stRetinaNnieParam, &stInputDataIdx, &stProcSegIdx, HI_TRUE);
        SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
            "Error,SAMPLE_SVP_NNIE_Forward failed!\n");
        PrintTime("NNIE forward", 0, HI_TRUE, 1);
        
        // ***************       soft forward       ***************
        s32Ret = Retinanet_Soft(&s_stRetinaNnieParam, &stSoftParam);
        PrintTime("soft forward", HI_TRUE, HI_TRUE, 1);
        

        // ***************       print infor       ***************
        HI_FLOAT* final_result_tmp = (HI_FLOAT*)stSoftParam.ps32FinalBbox.u64VirAddr;
        fprintf(stSoftParam.fp_result, "%s", pcSrcFile);
        for(HI_U32 ii=0; ii<stSoftParam.final_detection_num; ++ii){
            float conf = 1.0f/(1+exp(-1*final_result_tmp[ii*6]));
            printf("   score: %.3f   class: %d   xyxy: %.3f  %.3f  %.3f  %.3f\n",  
                conf, (HI_S32)final_result_tmp[ii*6+1], final_result_tmp[ii*6+2], 
                final_result_tmp[ii*6+3], final_result_tmp[ii*6+4], final_result_tmp[ii*6+5]);
            fprintf(stSoftParam.fp_result, " %.4f %d %d %d %d %d", conf, (HI_S32)final_result_tmp[ii*6+1], 
                    (HI_S32)final_result_tmp[ii*6+2], (HI_S32)final_result_tmp[ii*6+3], 
                    (HI_S32)final_result_tmp[ii*6+4], (HI_S32)final_result_tmp[ii*6+5]); // [img_name, score, class, x1, y1, x2, y2]
            // fprintf(stSoftParam.fp_result,"%s %.4f %d %d %d %d %d\n", pcSrcFile, final_result_tmp[ii*6], (HI_S32)final_result_tmp[ii*6+1], 
            //         (HI_S32)final_result_tmp[ii*6+2], (HI_S32)final_result_tmp[ii*6+3], 
            //         (HI_S32)final_result_tmp[ii*6+4], (HI_S32)final_result_tmp[ii*6+5]); // [img_name, score, class, x1, y1, x2, y2]
        } 
        fprintf(stSoftParam.fp_result, "\n");
        PrintTime("not print", 0, 0, 0);

    }

Retina_FAIL_0:
    Retinanet_HandleSig();
}


/* *****************************************************

本地读取jpg list,
VO显示检测结果

******************************************************* */
HI_U32 u32VdecChnNum = 1; // vdec通道数
SAMPLE_VO_CONFIG_S stVoConfig;
VPSS_GRP VpssGrp;
HI_BOOL abChnEnable[VPSS_MAX_CHN_NUM]; // 给VPSS哪些通道使能 标志位
void RetinaNet_JPG_to_Vo(void)
{
    HI_S32 s32Ret = HI_SUCCESS;

    HI_CHAR *pcModelName = "./retinanet/model/mbl_20_inst.wk";
    // HI_CHAR *pcModelName = "./retinanet/model/res_inst20.wk";
    // HI_CHAR *pcModelName = "./retinanet/model/mbl_nobn_20_inst.wk";
    // HI_CHAR *pcModelName = "./retinanet/model/res_nobn_20_inst.wk";

    /********    初始化vb    *********/
    SAMPLE_SVP_TRACE_INFO("vb init!\n");
    VB_CONFIG_S stVbConfig;
    memset(&stVbConfig, 0, sizeof(VB_CONFIG_S));
    stVbConfig.u32MaxPoolCnt             = 1; // 系统可容纳缓存池个数
    stVbConfig.astCommPool[0].u32BlkCnt  = 10*u32VdecChnNum; // 每个缓存池的缓存块个数
    stVbConfig.astCommPool[0].u64BlkSize = COMMON_GetPicBufferSize(1920, 1080,
                                                PIXEL_FORMAT_YVU_SEMIPLANAR_420, DATA_BITWIDTH_8, COMPRESS_MODE_SEG, 0); // 缓存块大小
                                                
    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConfig);
    if(s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("init sys fail for %#x!\n", s32Ret);
        goto END1;
    }

    /********    设置vdec的vb    *********/
    SAMPLE_SVP_TRACE_INFO("vdec vb init!\n");
    SAMPLE_VDEC_ATTR astSampleVdec[VDEC_MAX_CHN_NUM];
    HI_S32 i;
    for(i=0; i<u32VdecChnNum; i++) // 有一个vdec通道
    {
        astSampleVdec[i].enType                            = PT_JPEG;
        astSampleVdec[i].u32Width                          = 3840;
        astSampleVdec[i].u32Height                         = 2160;
        astSampleVdec[i].enMode                            = VIDEO_MODE_FRAME;
        astSampleVdec[i].stSapmleVdecPicture.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
        astSampleVdec[i].stSapmleVdecPicture.u32Alpha      = 255;
        astSampleVdec[i].u32DisplayFrameNum                = 2;
        astSampleVdec[i].u32FrameBufCnt                    = astSampleVdec[i].u32DisplayFrameNum + 1;
    }
    s32Ret = SAMPLE_COMM_VDEC_InitVBPool(u32VdecChnNum, &astSampleVdec[0]); // 根据类型、大小,设置公共视频缓存池属性(3个API)
    if(s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("init mod common vb fail for %#x!\n", s32Ret);
        goto END2;
    }

    /********    开启vdec    *********/
    SAMPLE_SVP_TRACE_INFO("start vdec!\n");
    s32Ret = SAMPLE_COMM_VDEC_Start(u32VdecChnNum, &astSampleVdec[0]);
    if(s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("start VDEC fail for %#x!\n", s32Ret);
        goto END3;
    }

    /********    开启vpss    *********/
    SAMPLE_SVP_TRACE_INFO("start vpss!\n");
    // 用于创建vpss的属性
    VPSS_GRP_ATTR_S stVpssGrpAttr; // vpss组属性
    stVpssGrpAttr.u32MaxW = 3840; // 3840 输入图像
    stVpssGrpAttr.u32MaxH = 2160; // 2160
    stVpssGrpAttr.stFrameRate.s32SrcFrameRate = -1; // 组帧率
    stVpssGrpAttr.stFrameRate.s32DstFrameRate = -1;
    stVpssGrpAttr.enDynamicRange = DYNAMIC_RANGE_SDR8; // 输入图像动态范围
    stVpssGrpAttr.enPixelFormat  = PIXEL_FORMAT_YVU_SEMIPLANAR_420; // 输入图像像素格式
    stVpssGrpAttr.bNrEn   = HI_FALSE; // NR 使能开关

    // 给VPSS哪些通道使能 标志位
    memset(abChnEnable, 0, sizeof(abChnEnable));

    VPSS_CHN_ATTR_S astVpssChnAttr[VPSS_MAX_CHN_NUM]; // vpss通道属性
    astVpssChnAttr[0].u32Width                    = RETINA_INPUT_SIZE; // 输出图片宽/高
    astVpssChnAttr[0].u32Height                   = RETINA_INPUT_SIZE;

    astVpssChnAttr[1].u32Width                    = 1920; // 输出图片宽/高
    astVpssChnAttr[1].u32Height                   = 1080;
    for(i = 0; i < 2; i++)
    {
        abChnEnable[i] = HI_TRUE; // 第0个vpss通道使能控制位

        astVpssChnAttr[i].enChnMode                   = VPSS_CHN_MODE_USER; // VPSS_CHN_MODE_AUTO
        astVpssChnAttr[i].enCompressMode              = COMPRESS_MODE_NONE; // 目标图像压缩模式 COMPRESS_MODE_SEG
        astVpssChnAttr[i].enDynamicRange              = DYNAMIC_RANGE_SDR8; // 目标图像动态范围
        astVpssChnAttr[i].enPixelFormat               = PIXEL_FORMAT_YVU_SEMIPLANAR_420; // 目标图像像素格式
        astVpssChnAttr[i].stFrameRate.s32SrcFrameRate = -1; // 帧率控制信息
        astVpssChnAttr[i].stFrameRate.s32DstFrameRate = -1;
        astVpssChnAttr[i].u32Depth                    = 1; // 用户获取通道图像的队列长度 0
        astVpssChnAttr[i].bMirror                     = HI_FALSE; // 水平镜像使能
        astVpssChnAttr[i].bFlip                       = HI_FALSE; // 垂直翻转使能
        astVpssChnAttr[i].stAspectRatio.enMode        = ASPECT_RATIO_NONE; // 幅形比参数
        astVpssChnAttr[i].enVideoFormat               = VIDEO_FORMAT_LINEAR; // 目标图像视频格式
    }

    for(i=0; i<2; i++) // 两个通道 
    {
        VpssGrp = i;
        s32Ret = SAMPLE_COMM_VPSS_Start(VpssGrp, &abChnEnable[0], &stVpssGrpAttr, &astVpssChnAttr[0]); // 开启vpss通道
        if(s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("start VPSS fail for %#x!\n", s32Ret);
            goto END4;
        }
    }

    /********    开启vo    *********/
    SAMPLE_SVP_TRACE_INFO("start vo!\n");
    VO_INTF_SYNC_E enIntfSync = VO_OUTPUT_1080P60; // VO_OUTPUT_1080P30 这个可以显示; VO_OUTPUT_1080P60
    PIC_SIZE_E enDispPicSize = PIC_1080P;
    stVoConfig.VoDev                 = SAMPLE_VO_DEV_UHD;
    stVoConfig.enVoIntfType          = VO_INTF_HDMI;
    stVoConfig.enIntfSync            = enIntfSync;
    stVoConfig.enPicSize             = enDispPicSize;
    stVoConfig.u32BgColor            = COLOR_RGB_BLUE;
    stVoConfig.u32DisBufLen          = 3;
    stVoConfig.enDstDynamicRange     = DYNAMIC_RANGE_SDR8;
    stVoConfig.enVoMode              = VO_MODE_1MUX;
    stVoConfig.enPixFormat           = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
    stVoConfig.stDispRect.s32X       = 0;
    stVoConfig.stDispRect.s32Y       = 0;
    stVoConfig.stDispRect.u32Width   = 1920;
    stVoConfig.stDispRect.u32Height  = 1080;
    stVoConfig.stImageSize.u32Width  = 1920;
    stVoConfig.stImageSize.u32Height = 1080;
    stVoConfig.enVoPartMode          = VO_PART_MODE_SINGLE;
    s32Ret = SAMPLE_COMM_VO_StartVO(&stVoConfig);
    if(s32Ret != HI_SUCCESS)
    {
        SAMPLE_PRT("start VO fail for %#x!\n", s32Ret);
        goto END5;
    }

    /********    vdec连接vpss    *********/
    SAMPLE_SVP_TRACE_INFO("vdec link vpss!\n");
    for(i=0; i<u32VdecChnNum; i++)
    {
        s32Ret = SAMPLE_COMM_VDEC_Bind_VPSS(i, i);
        if(s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("vdec bind vpss fail for %#x!\n", s32Ret);
            goto END6;
        }
    }
    usleep(10); 

    /********    发送数据到vdec    *********/
    SAMPLE_SVP_TRACE_INFO("set vdec para!\n");
    VDEC_THREAD_PARAM_S stVdecSend[VDEC_MAX_CHN_NUM];
    for(i=0; i<u32VdecChnNum; i++)
    { 
        snprintf(stVdecSend[i].cFileName, sizeof(stVdecSend[i].cFileName), "1080P.jpg");
        snprintf(stVdecSend[i].cFilePath, sizeof(stVdecSend[i].cFilePath), ".");
        stVdecSend[i].enType          = astSampleVdec[i].enType;
        stVdecSend[i].s32StreamMode   = astSampleVdec[i].enMode;
        stVdecSend[i].s32ChnId        = i;
        stVdecSend[i].s32IntervalTime = 1000;
        stVdecSend[i].u64PtsInit      = 0;
        stVdecSend[i].u64PtsIncrease  = 0;
        stVdecSend[i].eThreadCtrl     = THREAD_CTRL_START;
        stVdecSend[i].bCircleSend     = HI_TRUE;
        stVdecSend[i].s32MilliSec     = 0;
        stVdecSend[i].s32MinBufSize   = (astSampleVdec[i].u32Width * astSampleVdec[i].u32Height * 3)>>1;
    }



    /************************************************
    step: SVP
    *************************************************/

    // ***************       load wk       ***************
    // SAMPLE_SVP_TRACE_INFO("Load retinanet model!\n");
    s32Ret = SAMPLE_COMM_SVP_NNIE_LoadModel(pcModelName,&s_stRetinaModel);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_LoadModel failed!\n");
    PrintTime("load wk", 0, 0, 1);
    
    // ***************       NNIE init       ***************
    // SAMPLE_SVP_TRACE_INFO("retina NNIE parameter init!\n");
    SAMPLE_SVP_NNIE_CFG_S   stNnieCfg = {0};
    stNnieCfg.u32MaxInputNum = 1; // max input image num in each batch
    stNnieCfg.u32MaxRoiNum = 0;
    stNnieCfg.aenNnieCoreId[0] = SVP_NNIE_ID_0; // set NNIE core
    s_stRetinaNnieParam.pstModel = &s_stRetinaModel.stModel;
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamInit(&stNnieCfg, &s_stRetinaNnieParam);
    SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error(%#x),SAMPLE_COMM_SVP_NNIE_ParamInit failed!\n",s32Ret);
    PrintTime("NNIE init", 0, 0, 1);

    // ***************       soft init       ***************
    {
        HI_U64 u64PhyAddr = 0;
        HI_U8* pu8VirAddr = NULL;

        stSoftParam.ps32ResultMem.u32Size = SAMPLE_SVP_NNIE_ALIGN16(SAVE_RESULT_NUM_MAX * 6 * sizeof(HI_FLOAT));
        stSoftParam.ps32FinalBbox.u32Size = SAMPLE_SVP_NNIE_ALIGN16(DETECTION_RESULT_NUM_MAX * 6 * sizeof(HI_FLOAT));
        HI_U32 u32TotalSize = stSoftParam.ps32ResultMem.u32Size + stSoftParam.ps32FinalBbox.u32Size;

        s32Ret = SAMPLE_COMM_SVP_MallocCached("SAMPLE_SSD_INIT", NULL, (HI_U64*)&u64PhyAddr, (void**)&pu8VirAddr, u32TotalSize);
        SAMPLE_SVP_CHECK_EXPR_GOTO(HI_SUCCESS != s32Ret,Retina_FAIL_0,SAMPLE_SVP_ERR_LEVEL_ERROR, "Error,Malloc memory failed!\n");
        memset(pu8VirAddr, 0, u32TotalSize);
        SAMPLE_COMM_SVP_FlushCache(u64PhyAddr, (void*)pu8VirAddr, u32TotalSize);

        stSoftParam.ps32ResultMem.u64PhyAddr = u64PhyAddr;
        stSoftParam.ps32ResultMem.u64VirAddr = (HI_U64)(pu8VirAddr);

        stSoftParam.ps32FinalBbox.u64PhyAddr = u64PhyAddr + stSoftParam.ps32ResultMem.u32Size;
        stSoftParam.ps32FinalBbox.u64VirAddr = (HI_U64)(pu8VirAddr + stSoftParam.ps32ResultMem.u32Size);
    }
    PrintTime("soft init", 0, 0, 1);

    /********    开启检测线程    *********/
    pthread_t   VdecThread;
    myRETINA_THREAD retina_thred_para;
    retina_thred_para.pstVdecSend = &stVdecSend[0];
    retina_thred_para.pstMobileSsdNnieParam = &s_stRetinaNnieParam;
    retina_thred_para.pstMobileSsdSoftwareParam = &stSoftParam;

    mySAMPLE_COMM_VDEC_StartSendStream(u32VdecChnNum, &VdecThread, &retina_thred_para); // 开启发送图像+检测线程
    
    SAMPLE_COMM_VDEC_CmdCtrl(u32VdecChnNum, &stVdecSend[0], &VdecThread); // 控制函数 e退出 p暂停 r恢复

    mySAMPLE_COMM_VDEC_StopSendStream(u32VdecChnNum, &stVdecSend[0], &VdecThread); // 停止读取JPG


Retina_FAIL_0: // 释放svp
    // NNIE硬件释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamDeinit(&s_stRetinaNnieParam);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_ParamDeinit failed!\n");
    memset(&s_stRetinaNnieParam, 0, sizeof(SAMPLE_SVP_NNIE_PARAM_S));

    // soft内存释放
    if(0 != stSoftParam.ps32ResultMem.u64PhyAddr && 0 != stSoftParam.ps32ResultMem.u64VirAddr)
    {
        SAMPLE_SVP_MMZ_FREE(stSoftParam.ps32ResultMem.u64PhyAddr, stSoftParam.ps32ResultMem.u64VirAddr);
        stSoftParam.ps32ResultMem.u64PhyAddr = 0;
        stSoftParam.ps32ResultMem.u64VirAddr = 0;
    }
    memset(&stSoftParam, 0, sizeof(SVP_SOFT_RETINA_S));

    // 模型参数内存释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_UnloadModel(&s_stRetinaModel);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_UnloadModel failed!\n");
    memset(&s_stRetinaModel, 0, sizeof(SAMPLE_SVP_NNIE_MODEL_S));

END6: // 断开 vpss vdec
    for(i=0; i<u32VdecChnNum; i++)
    {
        s32Ret = SAMPLE_COMM_VDEC_UnBind_VPSS(i, i); // 使vdec和vpss断连
        if(s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("vdec unbind vpss fail for %#x!\n", s32Ret);
        }
    }

END5:  // 退出Vo
    SAMPLE_COMM_VO_StopVO(&stVoConfig);

END4: // 关闭vpss
    for(i = VpssGrp; i >= 0; i--) // i不能为unsigned int型
    {
        VpssGrp = i;
        SAMPLE_COMM_VPSS_Stop(VpssGrp, &abChnEnable[0]); // 关闭所有group的vpss
    }

END3: // 停止vdec
    SAMPLE_COMM_VDEC_Stop(u32VdecChnNum);

END2: // 退出vdec设置的vb
    SAMPLE_COMM_VDEC_ExitVBPool();

END1: // 退出vb
    SAMPLE_COMM_SYS_Exit();
}


void RetinaNet(){
    HI_S32 flag = TASK_SELECT;
    switch (flag)
    {
        case RetinaNet_aBGR_to_print_select:
            RetinaNet_aBGR_to_print();  // 测试一幅图 bgr -> print
            break;
        case RetinaNet_BGR_to_List_select:
            RetinaNet_BGR_to_List(); // bgr list -> txt
            break;
        case RetinaNet_JPG_to_Vo_select:
            RetinaNet_JPG_to_Vo(); // bgr list -> txt
            break;
        default:
            break;
    }
}



/* *****************************************************

HandleSig函数罗列

******************************************************* */
void RetinaNet_aBGR_to_print_HandleSig(void){
    HI_S32 s32Ret = HI_SUCCESS;

    // NNIE硬件释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamDeinit(&s_stRetinaNnieParam);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_ParamDeinit failed!\n");
    memset(&s_stRetinaNnieParam, 0, sizeof(SAMPLE_SVP_NNIE_PARAM_S));

    // soft内存释放
    if(0 != stSoftParam.ps32ResultMem.u64PhyAddr && 0 != stSoftParam.ps32ResultMem.u64VirAddr)
    {
        SAMPLE_SVP_MMZ_FREE(stSoftParam.ps32ResultMem.u64PhyAddr, stSoftParam.ps32ResultMem.u64VirAddr);
        stSoftParam.ps32ResultMem.u64PhyAddr = 0;
        stSoftParam.ps32ResultMem.u64VirAddr = 0;
    }
    memset(&stSoftParam, 0, sizeof(SVP_SOFT_RETINA_S));

    // 模型参数内存释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_UnloadModel(&s_stRetinaModel);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_UnloadModel failed!\n");
    memset(&s_stRetinaModel, 0, sizeof(SAMPLE_SVP_NNIE_MODEL_S));

    SAMPLE_COMM_SVP_CheckSysExit();
}

void RetinaNet_BGR_to_List_HandleSig(void){
    HI_S32 s32Ret = HI_SUCCESS;

    fclose(stSoftParam.fp_result);
    stSoftParam.fp_result = NULL;
    
    // NNIE硬件释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamDeinit(&s_stRetinaNnieParam);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_ParamDeinit failed!\n");
    memset(&s_stRetinaNnieParam, 0, sizeof(SAMPLE_SVP_NNIE_PARAM_S));

    // soft内存释放
    if(0 != stSoftParam.ps32ResultMem.u64PhyAddr && 0 != stSoftParam.ps32ResultMem.u64VirAddr)
    {
        SAMPLE_SVP_MMZ_FREE(stSoftParam.ps32ResultMem.u64PhyAddr, stSoftParam.ps32ResultMem.u64VirAddr);
        stSoftParam.ps32ResultMem.u64PhyAddr = 0;
        stSoftParam.ps32ResultMem.u64VirAddr = 0;
    }
    memset(&stSoftParam, 0, sizeof(SVP_SOFT_RETINA_S));

    // 模型参数内存释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_UnloadModel(&s_stRetinaModel);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_UnloadModel failed!\n");
    memset(&s_stRetinaModel, 0, sizeof(SAMPLE_SVP_NNIE_MODEL_S));

    SAMPLE_COMM_SVP_CheckSysExit();
}

void RetinaNet_JPG_to_Vo_HandleSig(void){
    HI_S32 s32Ret = HI_SUCCESS;

    // NNIE硬件释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_ParamDeinit(&s_stRetinaNnieParam);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_ParamDeinit failed!\n");
    memset(&s_stRetinaNnieParam, 0, sizeof(SAMPLE_SVP_NNIE_PARAM_S));

    // soft内存释放
    if(0 != stSoftParam.ps32ResultMem.u64PhyAddr && 0 != stSoftParam.ps32ResultMem.u64VirAddr)
    {
        SAMPLE_SVP_MMZ_FREE(stSoftParam.ps32ResultMem.u64PhyAddr, stSoftParam.ps32ResultMem.u64VirAddr);
        stSoftParam.ps32ResultMem.u64PhyAddr = 0;
        stSoftParam.ps32ResultMem.u64VirAddr = 0;
    }
    memset(&stSoftParam, 0, sizeof(SVP_SOFT_RETINA_S));

    // 模型参数内存释放
    s32Ret = SAMPLE_COMM_SVP_NNIE_UnloadModel(&s_stRetinaModel);
    SAMPLE_SVP_CHECK_EXPR_TRACE(HI_SUCCESS != s32Ret,SAMPLE_SVP_ERR_LEVEL_ERROR,
        "Error,SAMPLE_COMM_SVP_NNIE_UnloadModel failed!\n");
    memset(&s_stRetinaModel, 0, sizeof(SAMPLE_SVP_NNIE_MODEL_S));

    // 断开 vpss vdec
    for(int i=0; i<u32VdecChnNum; i++)
    {
        s32Ret = SAMPLE_COMM_VDEC_UnBind_VPSS(i, i); // 使vdec和vpss断连
        if(s32Ret != HI_SUCCESS)
        {
            SAMPLE_PRT("vdec unbind vpss fail for %#x!\n", s32Ret);
        }
    }

    // 退出Vo
    SAMPLE_COMM_VO_StopVO(&stVoConfig);

    // 关闭vpss
    for(int i = VpssGrp; i >= 0; i--) // i不能为unsigned int型
    {
        VpssGrp = i;
        SAMPLE_COMM_VPSS_Stop(VpssGrp, &abChnEnable[0]); // 关闭所有group的vpss
    }

    // 停止vdec
    SAMPLE_COMM_VDEC_Stop(u32VdecChnNum);

    // 退出vdec设置的vb
    SAMPLE_COMM_VDEC_ExitVBPool();

    // 退出vb
    SAMPLE_COMM_SYS_Exit();
}

void Retinanet_HandleSig(void)
{
    HI_S32 flag = TASK_SELECT;
    switch (flag)
    {
        case RetinaNet_aBGR_to_print_select:
            RetinaNet_aBGR_to_print_HandleSig();  // 测试一幅图 bgr -> print
            break;
        case RetinaNet_BGR_to_List_select:
            RetinaNet_BGR_to_List_HandleSig(); // bgr list -> txt
            break;
        case RetinaNet_JPG_to_Vo_select:
            RetinaNet_JPG_to_Vo_HandleSig(); // bgr list -> txt
            break;
        default:
            break;
    }
}









#ifdef __cplusplus
}
#endif
